package net.butfly.bus.filter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.butfly.bus.Request;
import net.butfly.bus.Response;
import net.butfly.bus.config.bean.FilterBean;
import net.butfly.bus.config.bean.FilterBean.Signal;
import net.butfly.bus.context.Constants;
import net.butfly.bus.exception.BusRuntimeException;

public class FilterChain {
	private List<FilterBean> beforeList = null;
	private List<FilterBean> afterList = null;

	public FilterChain(List<FilterBean> filterBeans) {
		if (filterBeans != null) {
			int pos, size = filterBeans.size();
			for (pos = 0; pos < size; pos++)
				if (filterBeans.get(pos).getOrder() >= 0) break;
			if (pos > 0) {
				this.beforeList = new ArrayList<FilterBean>(pos);
				this.beforeList.addAll(filterBeans.subList(0, pos));
				Collections.sort(beforeList);
				for (FilterBean fb : beforeList) {
					try {
						fb.getFilter().init();
					} catch (Throwable ex) {
						throw new BusRuntimeException(Constants.UserError.FILTER_INVOKE,
								"Failed in initialization of filter " + fb.getFilterClz()
										+ ", please check the class or filter configuration.", ex);
					}
				}
			}
			size--;
			if (pos < size) {
				this.afterList = new ArrayList<FilterBean>(size - pos);
				this.afterList.addAll(filterBeans.subList(pos, size));
				Collections.sort(afterList);
				for (FilterBean fb : afterList) {
					try {
						fb.getFilter().init();
					} catch (Throwable ex) {
						throw new BusRuntimeException(Constants.UserError.FILTER_INVOKE,
								"Failed in instantiation of filter " + fb.getFilterClz()
										+ ", please check the class or filter configuration.", ex);
					}
				}
			}
		}
	}

	public void execute(Request request, Response response, FilterBase base) throws Exception {
		FilterBean.Signal signal = FilterBean.Signal.goon;
		if (beforeList != null) {
			for (FilterBean fb : beforeList) {
				executeFilter(fb, request, response, signal);
			}
		}

		executeBase(base, request, response, signal);

		if (afterList != null) {
			for (FilterBean fb : afterList) {
				executeFilter(fb, request, response, signal);
			}
		}
	}

	private void executeBase(FilterBase base, Request request, Response response, Signal signal) throws Exception {
		switch (signal) {
		case goon:
		case toMain:
			base.execute(request, response);
			break;
		case skipNext:
			signal = FilterBean.Signal.goon;
			break;
		default:
			// TODO define the exception?
			// throw new Exception();
		}

	}

	private void executeFilter(FilterBean fb, Request request, Response response, FilterBean.Signal signal)
			throws Exception {
		switch (signal) {
		case goon:
			try {
				fb.getFilter().execute(request, response);
			} catch (Exception _ex) {
				signal = fb.getExceptionSignal();
				if (signal.equals(FilterBean.Signal.quit)) { throw _ex; }
			}
			break;
		case skipNext:
			signal = FilterBean.Signal.goon;
			break;
		default:
			// TODO define the exception?
			// throw new Exception();
		}
	}

}
